from flask import Flask, redirect, url_for, request
from sqlalchemy import inspect
from datetime import datetime, timedelta
from pathlib import Path
from models import ScheduledEvent, MonthlyBudget, Expense, db
from core import expense, budget, scheduled_event
import os
import threading
import time
import json
import logging

# 创建Flask应用
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(expense.expenses_bp)
app.register_blueprint(budget.budget_bp)
app.register_blueprint(scheduled_event.scheduled_events_bp)

# 确保 DATABASE_URL 被正确读取
DATABASE_URL = os.getenv("DATABASE_URL")
islocaltion = not DATABASE_URL
if islocaltion:
    # 获取当前工作目录
    basedir = os.path.abspath(os.path.dirname(__file__))
    # 确保 instance 目录存在
    os.makedirs(os.path.join(basedir, "instance"), exist_ok=True)
    dbpath = Path(basedir) / "instance" / "app.sqlite"
    # 如果环境变量未设置，使用本地 SQLite 文件
    DATABASE_URL = f"sqlite:///{dbpath}"

# 初始化APP配置
app.config["SQLALCHEMY_DATABASE_URI"] = DATABASE_URL
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
if islocaltion:
    app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {"isolation_level": "SERIALIZABLE"}
else:
    app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {"isolation_level": "READ COMMITTED"}

db.init_app(app)

# 打印数据库连接信息
initialized = False


def initialize_database():
    with app.app_context():
        # 检查所有需要的表是否存在
        inspector = inspect(db.engine)
        existing_tables = inspector.get_table_names()
        model_tables = ["fixed_expense", "monthly_budget", "expense", "scheduled_event"]

        # 检查所有模型表是否都存在于数据库中
        if all(table in existing_tables for table in model_tables):
            print("All tables exist, skipping initialization")
            return

        # 如果有任何表缺失，则创建所有表
        db.create_all()  # 注意：这不会删除现有表
        print("Database initialized with missing tables")


@app.before_request
def before_any_request():
    global initialized
    if not initialized:
        initialize_database()
        initialized = True


@app.route("/")
def home():
    return redirect(url_for("expenses.expenses"))


@app.route("/reset-db")
def reset_db():
    try:
        # 强制重置数据库：先删除所有表再重新创建
        db.drop_all()
        db.create_all()
        logging.info("有人重置了数据库")
        return redirect(url_for("expenses.expenses"))
    except Exception as e:
        return f"数据库重置失败: {str(e)}", 500


@app.route("/backup-db")
def backup_db():
    try:
        # 获取所有表的数据
        backup_data = {}

        # 备份预算数据
        budgets = MonthlyBudget.query.all()
        backup_data["budgets"] = [
            {
                "id": budget.id,
                "budget": budget.budget,
                "remaining": budget.remaining,
                "month": budget.month,
                "year": budget.year,
            }
            for budget in budgets
        ]

        # 备份支出数据
        expenses = Expense.query.all()
        backup_data["expenses"] = [
            {
                "id": expense.id,
                "amount": expense.amount,
                "date": expense.date.isoformat() if expense.date else None,
                "month": expense.month,
                "year": expense.year,
                "description": expense.description,
            }
            for expense in expenses
        ]

        # 备份定时事件数据
        scheduled_events = ScheduledEvent.query.all()
        backup_data["scheduled_events"] = [
            {
                "id": event.id,
                "interval_days": event.interval_days,
                "execution_time": (
                    event.execution_time.isoformat() if event.execution_time else None
                ),
                "event_type": event.event_type,
                "amount": event.amount,
                "last_executed": (
                    event.last_executed.isoformat() if event.last_executed else None
                ),
                "description": event.description,
                "skip_weekends": event.skip_weekends,
                "enabled": event.enabled,
            }
            for event in scheduled_events
        ]

        # 将数据转换为JSON字符串
        backup_json = json.dumps(backup_data, indent=2, ensure_ascii=False)

        # 创建响应
        from flask import Response

        return Response(
            backup_json,
            mimetype="application/json",
            headers={
                "Content-Disposition": "attachment; filename=wallet_records_backup.json"
            },
        )
    except Exception as e:
        return f"数据库备份失败: {str(e)}", 500


@app.route("/import-db", methods=["GET", "POST"])
def import_db():
    if request.method == "GET":
        return """
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>导入数据库</title>
            <link rel="stylesheet" href="/static/style.css">
        </head>
        <body>
            <h1>导入数据库</h1>
            <form method="POST" enctype="multipart/form-data">
                <div class="form-group">
                    <label for="backup_file">选择备份文件:</label>
                    <input type="file" id="backup_file" name="backup_file" accept=".json" required>
                </div>
                <div class="form-group">
                    <p class="warning">
                        警告：这将清除当前所有数据并导入新数据！
                    </p>
                </div>
                <button type="submit" class="btn" onclick="return confirm('确定要导入数据吗？这将删除所有现有数据！')">导入数据</button>
                <a href="/" class="btn">取消</a>
            </form>
        </body>
        </html>
        """

    try:
        # 获取上传的文件
        backup_file = request.files.get("backup_file")
        if not backup_file or backup_file.filename == "":
            return "未选择文件", 400

        # 读取并解析JSON数据
        backup_data = json.load(backup_file.stream)

        # 开始事务
        db.session.begin()

        try:
            # 清空现有数据
            ScheduledEvent.query.delete()
            MonthlyBudget.query.delete()
            Expense.query.delete()

            # 重置自增序列（仅适用于PostgreSQL）
            if not islocaltion:  # 如果不是本地SQLite
                from sqlalchemy import text

                db.session.execute(text("SELECT setval('expense_id_seq', 1, false)"))
                db.session.execute(
                    text("SELECT setval('monthly_budget_id_seq', 1, false)")
                )
                db.session.execute(
                    text("SELECT setval('scheduled_event_id_seq', 1, false)")
                )

            # 导入预算数据
            for budget_data in backup_data.get("budgets", []):
                budget = MonthlyBudget(
                    budget=budget_data["budget"],
                    remaining=budget_data["remaining"],
                    month=budget_data["month"],
                    year=budget_data["year"],
                )
                db.session.add(budget)

            # 导入支出数据
            for expense_data in backup_data.get("expenses", []):
                expense = Expense(
                    amount=expense_data["amount"],
                    date=(
                        datetime.strptime(expense_data["date"], "%Y-%m-%d").date()
                        if expense_data["date"]
                        else None
                    ),
                    month=expense_data["month"],
                    year=expense_data["year"],
                    description=expense_data["description"],
                )
                db.session.add(expense)

            # 导入定时事件数据
            for event_data in backup_data.get("scheduled_events", []):
                event = ScheduledEvent(
                    interval_days=event_data["interval_days"],
                    execution_time=(
                        datetime.strptime(
                            event_data["execution_time"], "%H:%M:%S"
                        ).time()
                        if event_data["execution_time"]
                        else None
                    ),
                    event_type=event_data["event_type"],
                    amount=event_data["amount"],
                    last_executed=(
                        datetime.strptime(
                            event_data["last_executed"], "%Y-%m-%d"
                        ).date()
                        if event_data["last_executed"]
                        else None
                    ),
                    description=event_data["description"],
                )
                if "skip_weekends" in event_data:
                    event.skip_weekends = event_data["skip_weekends"]
                if "enabled" in event_data:
                    event.enabled = event_data["enabled"]
                db.session.add(event)

            # 提交事务
            db.session.commit()
            return redirect(url_for("expenses.expenses"))
        except Exception as e:
            # 回滚事务
            db.session.rollback()
            raise e

    except Exception as e:
        return f"数据库导入失败: {str(e)}", 500


# 启动定时任务调度器
def execute_scheduled_events():
    """
    执行满足条件的定时事件
    """
    with app.app_context():
        try:
            # 获取所有启用的定时事件
            events = ScheduledEvent.query.filter_by(enabled=True).all()

            # 获取当前时间
            now = datetime.now()
            current_time = now.time().replace(second=0, microsecond=0)

            for event in events:
                # 检查是否应该执行该事件
                if should_execute_event(event, now, current_time):
                    try:
                        # 检查是否是周末且需要跳过周末 5是周六，6是周日
                        if not (event.skip_weekends and now.weekday() >= 5):
                            # 执行事件
                            if event.event_type == "income":
                                execute_income_event(event)
                            elif event.event_type == "expense":
                                execute_expense_event(event)

                        # 更新上次执行时间
                        event.last_executed = now.date()
                        db.session.commit()

                    except Exception as e:
                        logging.error(f"执行定时事件时出错 (ID: {event.id}): {str(e)}")
                        db.session.rollback()

        except Exception as e:
            logging.error(f"检查定时事件时出错: {str(e)}")


def should_execute_event(event, now, current_time):
    """
    判断是否应该执行某个事件
    """
    if event.last_executed is None:
        return current_time >= event.execution_time

    # 计算下次应该执行的时间
    next_execution_date = event.last_executed + timedelta(days=event.interval_days)
    next_execution = datetime.combine(next_execution_date, event.execution_time)

    # 检查是否到了执行时间
    if now >= next_execution:
        # 比较时间部分（忽略秒和微秒）
        return current_time >= event.execution_time

    return False


def execute_income_event(event):
    """
    执行收入事件：往当月预算中添加金额
    """
    now = datetime.now()
    month = now.month
    year = now.year

    # 查找当月预算
    budget = MonthlyBudget.query.filter_by(month=month, year=year).first()

    if budget:
        # 更新预算
        budget.budget += event.amount
        budget.remaining += event.amount
    else:
        # 如果没有预算记录，创建一个新的
        new_budget = MonthlyBudget(
            month=month, year=year, budget=event.amount, remaining=event.amount
        )
        db.session.add(new_budget)


def execute_expense_event(event):
    """
    执行支出事件：往支出中添加记录
    """
    now = datetime.now()
    month = now.month
    year = now.year

    # 创建支出记录
    expense = Expense(
        amount=event.amount,
        date=now.date(),
        month=month,
        year=year,
        description=event.description or f"定时支出事件 (ID: {event.id})",
    )

    db.session.add(expense)

    # 查找当月预算并更新
    budget = MonthlyBudget.query.filter_by(month=month, year=year).first()

    if budget:
        budget.remaining -= event.amount
    else:
        # 如果没有预算记录，创建一个新的（负余额）
        new_budget = MonthlyBudget(
            month=month, year=year, budget=0, remaining=-event.amount
        )
        db.session.add(new_budget)


def start_scheduler():
    """
    启动定时任务调度器
    """

    def run_scheduler():
        while True:
            try:
                execute_scheduled_events()
                # 每分钟检查一次
                time.sleep(60)
            except Exception as e:
                logging.error(f"定时任务调度器出错: {str(e)}")
                time.sleep(60)  # 出错后等待1分钟再继续

    # 在后台线程中运行调度器
    scheduler_thread = threading.Thread(target=run_scheduler, daemon=True)
    scheduler_thread.start()
    logging.info("定时任务调度器已启动")
